package org.jpropelleralt.utils.impl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Maintains counts associated with instances,
 * keeping strong links to those instances so that
 * they may will not be garbage collected.
 * 
 * NOT thread safe.
 * 
 * @param <T>		The type of object counted
 */
public class StrongCounter<T> implements Iterable<T> {

	/**
	 * Map from counted instances to counts. 
	 */
	private final Map<T, Integer> counts;
	
	/**
	 * Create a new {@link StrongCounter}
	 */
	public StrongCounter() {
		this(16);
	}
	
	/**
	 * Create a new {@link StrongCounter}
	 * @param initialCapacity		The initial instance capacity
	 */
	public StrongCounter(int initialCapacity) {
		counts = new HashMap<T, Integer>(initialCapacity);
	}
	
	/**
	 * Add to the count for an instance
	 * @param instance	The instance
	 * @return 			The new count for the instance - 1 if
	 * 					it has just been added for the first time.
	 */
	public int add(T instance) {
		
		//Get current count
		Integer count = counts.get(instance);
		
		//No mapping means 0 count
		if (count == null) {
			count = 0;
		}
		
		//We increment count
		count++;
		
		//Put count (back)
		counts.put(instance, count);
		
		return count;
	}
	
	/**
	 * Decrement the count for an instance
	 * @param instance	The instance
	 * @return 			The new count for the instance - 0 if it has
	 * 					just been removed completely. -1 if it did not
	 * 					have a count (that is, if it is being removed
	 * 					but was not present)
	 */
	public int remove(T instance) {
		
		//Get current count
		Integer count = counts.get(instance);
		
		//No mapping, so just return -1
		if (count == null) {
			return -1;
		}
		
		//Decrement count
		count--;
		
		//Put count (back) if it is positive, otherwise clear
		if (count > 0) {
			counts.put(instance, count);
		} else {
			counts.remove(instance);
		}
		
		return count;
	}
	
	/**
	 * Clear all counts
	 */
	public void clear() {
		counts.clear();
	}
	
	/**
	 * Iterator over all instances with a count
	 * @return An iterator of counted instances
	 */
	@Override
	public Iterator<T> iterator() {
		return new ImmutableIterator(counts.keySet().iterator());
	}
	
	/**
	 * Unpacks values from {@link WeakIdentityReference}s, and filters
	 * out unpacked null values (this occurs when the values are
	 * garbage collected).
	 * It also removes elements that refer to null, since they serve
	 * no purpose.
	 */
	private class ImmutableIterator implements Iterator<T> {

		private final Iterator<T> it;
		
		private ImmutableIterator(Iterator<T> it) {
			this.it = it;
		}
		
		@Override
		public boolean hasNext() {
			return it.hasNext();
		}

		@Override
		public T next() {
			return it.next();
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException("Cannot remove from StrongCounters iterator");
		}
		
	}
}
